<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>基础的虚拟滚动 ---- 不定高--- 动态虚拟滚动</title>
    <!-- 引入 Ant Design Vue 的样式文件 -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ant-design-vue/2.2.6/antd.min.css">
</head>

<body>
<div id="app">
    <div class="box" ref="box" @scroll="listScroll">
       <div class="item" v-for="(v, i) in showList"  :style="{ height: v.height + 'px' }"  :key="i">
           {{ v.name }}
       </div>
    </div>
</div>

<!-- 引入 Vue 和 Ant Design Vue 的脚本文件 -->
<script src="../vue.js"></script>

<script>

 // 不定高度，也即是每个itemHeight 的高度是不确定的，那么itemHeight 不好确定
 // 那么可以取渲染元素的最大高度作为itemHeight, 每一个item 如果可以确定高度，只是高度不一样，那么就可以去最大的item 的高度作为itemHeight

  // 终极解决方案


 // 唯一已知的信息
 const boxHeight = 300
 const itemHeight = boxHeight/6  // 实现不知道，那么只能预设值比如容器的6/1 ---- 边界线

 // 滚动极限值，也称为滚动分水岭的时候，取改变我们的list 数组
 // 未知的条数---- 比如说就先设置这个值 ---- 前提条件确保这个初始值渲染的items 的总高度要大于容器的高度
 // 可以通过预先渲染---不断重新设置去找，这里就不多叙述了
  let lastIndexInitalVal = 18

  new Vue({
    el: '#app',
    components: {},
    data() {
      return {
        beyondDistance: 25,
        lastIndex: lastIndexInitalVal,
        prevAdd: false,
        lastScrollTop: 0,
        manuStop: false,
        list: [
          { name: 1, height: 10 },
          { name: 2 , height: 10},
          { name: 3 , height: 10},
          { name: 4 , height: 10},
          { name: 5 , height: 10},
          { name: 6 , height: 10},
          { name: 7 , height: 10},
          { name: 8 , height: 50},
          { name: 9 , height: 50},
          { name: 10 , height: 40},
          { name: 11 , height: 90},
          { name: 12 , height: 30},
          { name: 13 , height: 50},
          { name: 14 , height: 70},
          { name: 15 , height: 50},
          { name: 16 , height: 100},
          { name: 17 , height: 80},
          { name: 18 , height: 10},
          { name: 19 , height: 180},
          { name: 20 , height: 90},
        ]
      };
    },
    computed: {
      // 实际展示的元素
      showList() {
        // 最小的元素编号，和0 取最大值，不可以比0 小
        const start = Math.max(this.lastIndex - lastIndexInitalVal, 0)
        const end = Math.min(this.lastIndex, this.list.length + 1)
        return this.list.slice(start, end)
      }
    },
    mounted() {
      setTimeout(()=> {
        this.calcBeyondDistance()
      }, 100)

    },
    methods: {
      // 计算最大的滚动距离
      calcBeyondDistance () {
        // 第一次渲染可以拿到初始化值的高度--- 作为标准, 要确保这个初始高度一定是要比容器高度大的
        const scrollHeight = this.$refs.box.scrollHeight
        // 最大滚动高度
        const result = Math.ceil(scrollHeight - boxHeight)
        this.beyondDistance = result
      },
      listScroll(e) {
        // 通过scrollTop 可以获取当前容器的滚动距离, 本质意思是被隐藏在内容区域上方的像素距离。设置该值可以改变元素的滚动位置
        // const scrollTop =  e.srcElement.scrollTop
        //  返回元素整体的高度，包括内边距，内容，边框，但是不包含外边距，包括由于 overflow 溢出而在屏幕上不可见的内容， 只读 它也包括::before和::after  这样伪元素的高度。
        // const scrollHeight = e.srcElement.scrollHeight
        const scrollTop =  e.srcElement.scrollTop
        const distance = scrollTop - this.lastScrollTop

        if (distance >= 0) { // 表示向下滑动 ---- 0 给了向下滑动，那么向上滑动就不得考虑极限值情况了
            // 已经滑动到最底部了---- 向下滑动的边界判断条件
              if (this.lastIndex === this.list.length) {
                // 重新计算最大滚动距离 ---- 为了让最后一个元素显示在底部，所以也要重新计算滚动距离
                this.calcBeyondDistance()
                e.srcElement.scrollTop = this.beyondDistance
                this.lastScrollTop = this.beyondDistance
                return
              }
              console.log('用户向下滚动了多少像素',  scrollTop)

              if (scrollTop >= (this.beyondDistance/2)) { // 当滑动距离到一个极限值的时候需要将滑动距离给重置回去,此时需要重新计算this.beyondDistanc
                this.lastIndex = this.lastIndex + 1
                e.srcElement.scrollTop = 0
                this.lastScrollTop = 0
                // 重新计算最大滚动距离
                this.calcBeyondDistance()
                return
              }

          // 更新下上次的坐标
          this.lastScrollTop = scrollTop

        } else {

          console.log('用户向上滚动了多少像素',  scrollTop)
          if (this.lastIndex === lastIndexInitalVal) {
            e.srcElement.scrollTop = 0
            this.lastScrollTop = 0
            // 重新计算最大滚动距离
            setTimeout(()=> {
              this.calcBeyondDistance()
            })
            return
          }
          if (scrollTop < (this.beyondDistance/2)) {
            this.lastIndex = this.lastIndex - 1
            // 重新计算最大滚动距离
            setTimeout(()=> {
              this.calcBeyondDistance()
              e.srcElement.scrollTop = this.beyondDistance
              this.lastScrollTop = this.beyondDistance + 1
            })
            return
          }

          this.lastScrollTop = scrollTop
        }

      }
    },
  });
</script>

<!-- 自定义的 CSS 样式 -->
<style>
    #app  {
        padding-top: 100px;
    }
    body {
        overflow: hidden;
    }
    .box {
        margin: 0 auto;
        margin-top: 150px;
        width: 200px;
        height: 300px;
        overflow: auto;
        border: 1px solid grey;
    }
    .item {
        border-bottom: 1px solid grey;
    }
</style>
</body>

</html>
